/*
 *   Copyright (C) 2007 by Andrzej Oczkowicz
 *   andrew_lz@poczta.fm
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *   KMediaCatalog :)
 *
 */
#include <QLibrary>

#include "kmodmgr.h"
#include "kutils.h"
#include "kmodule.h"
#include "kmaster.h"
#include "kconfig.h"
#include "dbg.h"
#include "kmediacatalog.h"
#include "modules/main/magic.h"

#ifndef MAIN_MODULE_MAGIC
#define MAIN_MODULE_MAGIC 0
#endif

kModMgr::kModMgr(QObject *parent)
 : QObject(parent)
{
}


kModMgr::~kModMgr()
{
  freeAllModules();
}

kModuleEntry * kModMgr::moduleGetEntry(const QString & mod)
{
  kModuleEntry * e = new kModuleEntry;
  e->exit = 0;
  e->init = 0;
  e->object = 0;
  e->magic = 0;
  e->lib = new QLibrary(mod);
  if ( e->lib->load() ){
    QString lib( e->lib->fileName() );
    e->init = (kModuleInitProc) e->lib->resolve(KMODULE_INIT_SYMBOL);
    if (!e->init){
      error(ERR_NO_MEMBER, QString(tr("Error resolving %1() symbol from module %2")).arg(KMODULE_INIT_SYMBOL).arg(lib) );   
      e->lib->unload();
      delete e->lib;
      delete e;
      return 0;
    }
    e->magic = modMagic(lib);
    _D << "magic: " << e->magic;
    e->exit = (kModuleExitProc)e->lib->resolve(KMODULE_EXIT_SYMBOL);
    return e;
  }
  delete e->lib;
  delete e;
  return 0; 
}

void kModMgr::modFree(kModuleEntry * e)
{
  if (e){
    if (e->exit)
      e->exit();
    if (e->lib){
      e->lib->unload();
      delete e->lib;
    }
    delete e;
  }
}

void kModMgr::modUnLoad(kModuleEntry * e)
{
  if (e){
    if ( e->exit )
      e->exit();
    else if ( e->object )
      delete e->object;
    e->object = 0;
  }
}

bool kModMgr::modLoad(kModuleEntry * e)
{
  if (e && e->init){
    e->object = e->init();
    return e->object != 0;
  }
  return false;
}

kMagic kModMgr::modMagic(const QString & mod)
{
  QFile f(mod);
  if ( ! f.open( QIODevice::ReadOnly ) )
    return 0;
  QDataStream s(&f);
  char *b = new char [ f.size() ];
  s.readRawData(b, f.size());
  return qChecksum(b, f.size());
}

bool kModMgr::loadMainModule()
{
  main = moduleGetEntry( kConfig::modulesDir(kConfig::pathSystem) + "kMainModule" );
  if (!main)
    main = moduleGetEntry( kConfig::modulesDir(kConfig::pathUser) + "kMainModule" );
  if (!main){
    _F << "Error loading main module!";
  } else {
    if ( main->magic != MAIN_MODULE_MAGIC ){
      _E << QString("Main module magic mismatch MAIN_MODULE_MAGIC:%1, this module magic:%2").arg(MAIN_MODULE_MAGIC).arg(main->magic).toAscii();
    }
  }
  modTryAdd(main);
  return main != 0;
}

void kModMgr::modTryAdd(kModuleEntry * e)
{
  if (!e)
    return;
  for (int i=0; i<modules.size(); i++){
    if ( modules.at(i)->magic == e->magic ){
      if ( modules.at(i)->lib && e->lib )
        _W << QString("Module '%3' (magic:%1) is alredy loaded as '%2'").arg(e->magic)
                                                                        .arg(modules.at(i)->lib->fileName() )
                                                                        .arg(e->lib->fileName() ).toAscii();
      return;
    }
  }
  if ( e->lib )
    _D << QString("Adding module: %1 (magic: %2)").arg( e->lib->fileName() ).arg( e->magic );
  modules.append(e);
}

void kModMgr::freeAllModules()
{
  for (int i=0; i<modules.size(); i++)
    modFree(modules[i]);
  modules.clear();

  listInBasic.clear();
  listInArch.clear();
  listOut.clear();
}

int kModMgr::findAllModules(const QString & dir)
{
    QDir files(dir);
    if (!files.isReadable()) {
        _W << "Error reading: " << dir.toLocal8Bit();
        return error(ERR_ACCESS, QString(tr("Error reading directory (access) '%1'!").arg(dir) ) );
    }
    files.setFilter( QDir::Files | QDir::Hidden | QDir::System );
    const QFileInfoList list(files.entryInfoList());
    for (int i=0; i < list.size(); i++) {
        kModuleEntry * e = moduleGetEntry( list[i].filePath() );
        if (e){
          modTryAdd(e);
        }
    }
  return 0;
}

void kModMgr::findModules()
{
  loadMainModule();
  findAllModules( kConfig::modulesDir(kConfig::pathSystem) );
  findAllModules( kConfig::modulesDir(kConfig::pathUser) );
  resolvePlugins();
}

void kModMgr::resolvePlugins()
{

  listInBasic.clear();
  listInArch.clear();
  listOut.clear();

  for (int i = 0; i < modules.size(); i++ ){
    kModuleEntry * e = modules.at(i);
    if ( e && modLoad(e) ){
      kPluginList * plugins;
      plugins = e->object->plugins();
      if (plugins){
        for (int x=0; x<plugins->size(); x++){
          kPlugin * plugin = plugins->at(x);
          if ( plugin ){
           ///@todo: kPluginConfig cfg = kConfig.getPluginConfig();
            kPluginConfig cfg;
            cfg.active = true;
            cfg.owner  = e->object;

            _D << QString("%1: mod: %2, %3 (%4) ").arg(plugin->baseType()).arg( e->object->name() ).arg( plugin->name() ).arg( plugin->description() ).toAscii();

            switch ( plugin->baseType() ) {
              case plugInputBasic:{
                      kInputBasicInfo info;
                      info.cfg = cfg;
                      info.plugin = (kInputBasic*)plugin;
                      listInBasic.append(info);
                      break;
                  };
              case plugInputArch:{
                      kInputArchiveInfo info;
                      info.cfg = cfg;
                      info.plugin = (kInputArchive*)plugin;
                      listInArch.append(info);
                      listInArch;
                      break;
                   };
              case plugOutput: {
                      kOutputPluginInfo info;
                      info.cfg = cfg;
                      info.plugin = (kOutputBasic*)plugin;
                      listOut.append(info);
                      break;
                   };
              default:{
                        _E << "Unknown plugin type: " << plugin->baseType() ;
                      } 
            }
          }
        }
      } else {
        _E << "no plugins in module";
      }
    }
  }
}

int kModMgr::processFile(QFileInfo * fi)
{
  kFileInfo f;
  f.fileInfo = fi;
  kInfoResult res;
  clearResStruct(res);
  kNode * node = 0;
  kBasicFileInfo fileInfo;
  for (int i=0; i<listInBasic.size(); i++){
    listInBasic.at(i).plugin->getFileInfo(&f, res);
    for (int x=0; x<listOut.size(); x++){
      kOutputBasic * out = listOut.at(x).plugin;
      node = out->createNode(fi);
      importedInfoToBasicInfo(fi, &res, fileInfo);
      out->addNodeData(node, &fileInfo);
      if (res.content){
        for (int c=0; x<res.content->size(); c++){
           kNode * subNode = out->createNode(node, &res.content->at(c) );
           out->addNodeData(subNode, &res.content->at(c) );
        }
      }
      out->freeNode(node);
    }
  }
}

int kModMgr::stopScanning()
{
  if ( listOut.size() > 0 )
    kMaster::getInstance()->mainWindow()->openCatalog(listOut.at(0).plugin, "");
}

int kModMgr::startScanning()
{
  rootNode = 0;
}

void kModMgr::importedInfoToBasicInfo(QFileInfo * fi, kInfoResult * res, kBasicFileInfo & basic)
{
  if ( !fi || !res )
    return;
  basic.import = res->info;
  basic.name = fi->fileName();
  basic.size = fi->size();
}

void kModMgr::clearResStruct(kInfoResult & res)
{
  res.content = 0;
  res.info.pluginName = QString::null;
  res.info.description = 0;
  res.info.icon = 0;
  res.info.thumbnail = 0;
}

